package com.luci.config;

import com.luci.security.CustomSecurityMetadataSource;
import com.luci.security.DefaultAccessDecisionManager;
import com.luci.security.DefaultProvider;
import com.luci.security.filter.AuthFilter;
import com.luci.security.filter.LoginFilter;
import com.luci.security.result.AuthResultAdapter;
import com.luci.security.result.DefaultAuthResult;
import com.luci.security.support.DefaultAuth;
import com.luci.security.support.DefaultLogin;
import com.luci.security.support.IAuth;
import com.luci.security.support.ILogin;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.List;

/**
 * WebSecurityConfig
 *
 * @author yuyanan
 * @version 1.0
 * @date 18-10-19
 */
@Slf4j
@Configuration
@EnableWebSecurity
@Order(Ordered.HIGHEST_PRECEDENCE)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements InitializingBean {

    @Autowired
    private ILogin iLogin;

    @Autowired
    private IAuth iAuth;

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    /**
     * 配置类
     *
     * @return AuthResultAdapter
     */
    @ConditionalOnMissingBean(AuthResultAdapter.class)
    @Bean
    public AuthResultAdapter authResultAdapet() {
        return new DefaultAuthResult();
    }

    /**
     * 配置类
     *
     * @return AuthProperties
     */
    @ConditionalOnMissingBean(AuthProperties.class)
    @Bean
    public AuthProperties authProperties() {
        AuthProperties authProperties = new AuthProperties();
        return authProperties;
    }

    /**
     * 配置类 登录支持类
     *
     * @return ILogin
     */
    @ConditionalOnMissingBean(ILogin.class)
    @Bean
    public ILogin iLogin() {
        ILogin iLogin = new DefaultLogin();
        return iLogin;
    }

    /**
     * 配置类 授权支持类
     *
     * @return ILogin
     */
    @ConditionalOnMissingBean(IAuth.class)
    @Bean
    public IAuth iAuth() {
        IAuth iAuth = new DefaultAuth();
        return iAuth;
    }

    /**
     * 配置不需要走过滤链的url ant风格 一般配置 js cs file 等静态资源
     *
     * @param web WebSecurity
     * @throws Exception Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {

        List<String> antUrlList = authProperties().getAntUrl();
        antUrlList.add("/404");
        antUrlList.add("/403");
        antUrlList.add("/500");
        antUrlList.add("/error");
        String[] antUrls = authProperties().getAntUrl().toArray(new String[antUrlList.size()]);
        web.ignoring().antMatchers(antUrls);
        super.configure(web);
    }

    /**
     * 配置过滤链
     *
     * @param http HttpSecurity
     * @throws Exception Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        if (authProperties().getCrossDomain()) {
            //关闭csrf拦截 支持跨域
            http.csrf().disable().cors();
        }

        if (authProperties().getSessionInvalid()) {
            // 基于token，所以不需要session
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }

        //配置url拦截器 FilterSecurityInterceptor
        http.authorizeRequests()
                //.antMatchers(antUrls).permitAll()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        return object;
                    }
                })
                //.accessDecisionManager(customAccessDecisionManager())
                //其他所有资源都需要认证，登陆后访问
                .anyRequest().authenticated();

        // 自定义认证后访问失败处理器
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
        // 匿名用户访问失败处理器
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint());

        //添加登录拦截
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);

        //授权拦截器
        http.addFilterAt(customSecurityFilterInterceptor(), FilterSecurityInterceptor.class);

        if (!authProperties().getHeaderCache()) {
            // 禁用缓存,不将头信息加入响应中
            http.headers().cacheControl();
        }
    }

//    @Bean
//    public AccessDecisionManager customAccessDecisionManager() {
//        List<AccessDecisionVoter<? extends Object>> decisionVoters
//            = Arrays.asList(
//            new WebExpressionVoter(),
//            // new RoleVoter(),
//            new RoleBasedVoter(),
//            new AuthenticatedVoter());
//        return new UnanimousBased(decisionVoters);
//    }

    /**
     * 授权拦截器
     *
     * @return AuthFilter
     */
    private AuthFilter customSecurityFilterInterceptor() {
        AuthFilter authFilter = new AuthFilter(accessDecisionManager(), customSecurityMetadataSource());
        authFilter.setiAuth(iAuth);
        return authFilter;
    }

    /**
     * 登录后访问接口鉴权失败处理器
     *
     * @return AccessDecisionManager
     */
    @Bean
    public AccessDecisionManager accessDecisionManager() {
        return new DefaultAccessDecisionManager(iAuth);
    }


    /**
     * 角色与url关系的数据源
     *
     * @return FilterInvocationSecurityMetadataSource
     */
    @Bean
    public FilterInvocationSecurityMetadataSource customSecurityMetadataSource() {
        return new CustomSecurityMetadataSource(iAuth);
    }

    /**
     * 登录拦截器
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter(authenticationManager(), authProperties(), iLogin, authResultAdapet());
        return loginFilter;
    }

    /**
     * login
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        AuthenticationProvider provider = new DefaultProvider(iLogin);
        auth.authenticationProvider(provider);
    }

    /**
     * 登录前访问授权接口处理器
     *
     * @return AuthenticationEntryPoint
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, authException) -> iAuth.anonymousDenied(request, response, authException);
    }

    /**
     * 登录后授权失败处理器
     *
     * @return AccessDeniedHandler
     */
    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return (request, response, accessDeniedException) -> iAuth.accessDenied(request, response,
                accessDeniedException);
    }


}
